package analisis_numerico_2;

import java.util.Random;


public class Main {

    static int n,indice,nn,algoritmo;
    static double[] X,b;
    static double[][]A;
    static Random r = new Random();
    
    /* PROGRAMA PRINCIPAL*/
    public static void main(String[] args) {

        /* Llama al procedimiento menu el cual permite elegir
         * una de las dos funciones
         * y al procedimiento menu2 que permite elegir
         * uno de los tres metodos*/
        System.out.println();
        System.out.println("TRABAJO Nº2 ANALISIS NUMERICO");
        indice = 2;//menu();
        if(indice ==1){ //Si vamos a a trabajar en la parte grupal
            n=menu2();
            System.out.println(-n);
            X = new double[n];
            A = new double [n][n];
            b= new double[n];
            nn = menu3();
            llenarMatriz(A,n,nn,b);           
            algoritmo = menu4();//elegimos que algoritmo vamos a usar
            if(algoritmo==1) Gauss(A,b,n);
            else GaussSeidel(A,b,n);
        }else{//si vamos a trabajar en la parte indivdual
            n=menu2();
            X = new double[n];
            A = new double [n][n];
            b= new double[n];
            llenarMatriz2(A,b,n);
            algoritmo = menu4();
            if(algoritmo==1) Gauss(A,b,n);
            else GaussSeidel(A,b,n);
        }
}
    
    static void llenarMatriz2(double[][] A,double[]b, int n){
        double q;
        int i,j;
        System.out.println("Ingrese el valor de q:");
        q=Leer.datoDouble();
        
        //ingresa los datos a la matriz dependiendo del q ingresado
        i=1;
        j=1;
        while((i<n) && (j<n)){//ingresamos los 1 a la diagonal
            A[i][j]=1;
            i++;
            j++;
        }
        i=0;
        j=1;
        while(i<n-2){
            while(j<n-1){
                A[i][j]=0;  //ingresamos los ceros 
                j++;
            }
            i++;
            j=i+1;
        }
        
        i=1;
        j=0;
        while(j<n-1){
            while(i<n){
                A[i][j]=-q;//ingresamos los -q
                i++;
            }
            j=i+1;
            i=j+1;
        }
        for(i=1;i<n;i++){
            for(j=0;j<i;j++)
                A[i][j]=-q;
            A[i][n-1] = q;
        }
        
        b[0]=11;
        b[1]=0;
        b[2]=1;
        b[3]= (-10*q-20);    //ingresamos los valores a la matriz b
        b[n-2]=25;   
        b[n-1]= (-30*q+10);
        for(i=4; i<n-2;i++){
            b[i]=1;
        }
        A[0][0]=q;
        A[0][n-1]=q;    //ingresamos a A los q
        A[n-1][n-1]=q;
        
                
    }
    
    
    
    //Algoritmo que calcula las incognitas del sistema de ecuaciones
    public static void GaussSeidel(double [][] A, double[] b, int n){
        double resultado=0;
        int iterada=0;
        // hacemos que todos los valores inciales sean o
        for(int i=0; i<n; i++){
            X[i]=0;
        }
        
        //repetir hasta la iterada 25
        //multiplicamos cada coeficiente de A por su respectivo Xi y se lo restamos al coefciente
        // de la misma fila de b, y luego dividimos por el coeficiente de xi
        while(iterada<25){
             for(int i=0;i<n;i++){
                for(int j=0; j<n;j++){
                    if(j!=i)
                        resultado = resultado + (A[i][j]*X[j]);
                 }
              X[i]=(b[i]-resultado)/(A[i][i]);
              
              resultado=0;
             }
             iterada++;
        }
        
        //imprimimos las matrices
	
	  System.out.println("La MATRIZ A ES");//imprimimos la matriz A
	  for(int i=0;i< n;i++){ //vamos manejando las filas
    	for(int j=0;j<n;j++){//vamos manejando las columnas
                //redondeamos los resultados a 3 decimales despues de la coma
    		A[i][j] = (double)Math.round(A[i][j]*1000) /1000;
    		System.out.print(" "+A[i][j]+"");//imprimimos la matriz
    	}//fin ciclo for columna
    	System.out.println(" |");//imprimimos un salto de linea
    }//fin ciclo for fila
	System.out.println("");//imprimimos un salto de linea
	
        
        System.out.println("LA MATRIZ B ES");//imprimimos la matriz B
    	for(int j=0;j<n;j++){//recorremos las filas
            //redondeamos los resultados a 3 decimales despues de la coma
    		b[j] = (double)Math.round(b[j]*1000) /1000;
    		System.out.println(" b"+(j+1)+" = " +""+b[j]+"");//imprimimos la matriz
    	}//fin for fila
    	System.out.println("");//imprimimos un salto de linea
        
        System.out.println("LA SOLUCION ES");//imprimimos la matriz X
    	for(int j=0;j< n;j++){//recorremos las filas
                //redondeamos los resultados a 3 decimales despues de la coma
                X[j] = (double)Math.round(X[j]*1000) /1000;
    		//imprimimos la solucion con su subindice
    		System.out.println(" X"+(j+1)+" = "+X[j]);
        }//fin del ciclo for
        
        
        
    
    }
    
    //menu en el cual nos devuelve una opcion para ver en que modo vamos a trabajar
    public static int menu(){
        
        System.out.println();
        System.out.println("ELIJA UNA OPCION");
        System.out.println("[1] Informe grupal");
        System.out.println("[2] Informe individual");
        return Leer.datoInt();
    }

    
    //menu en el cual ingresamos el numero de incognitas del sistema
    public static int menu2(){
        
        System.out.println();
        System.out.println("Ingrese el numero de incognitas n = ");
        System.out.println();
        return Leer.datoInt();
    }
    
    //menu en el cual decidimos si llenar la matriz por teclado o de forma automatica
    public static int menu3(){
        
        System.out.println();
        System.out.println("¿Ingresar de forma automatica o manual?");
        System.out.println("[1] Automatico");
        System.out.println("[2] Manual");
        return Leer.datoInt();
    }
    
    //menu en el cual decidimos por medio de que algoritmo vamos a resolver el sistema
    public static int menu4(){
        
        System.out.println();
        System.out.println("¿Que algoritmo desea usar?");
        System.out.println("[1] Algoritmo de Gauss con pivoteo por columnas");
        System.out.println("[2] Algoritmo de Gauss-Seidel");
        return Leer.datoInt();
    }
    
    
    
    //llenado de la matriz cuando vamos a trabajar en modo grupal
    //las llenas automaticamente o por teclado
    public static void llenarMatriz(double[][] A, int n,int nn, double[] b){
        
    
        if(nn==1){
            for(int i =0; i<n; i++){
                    for(int j=0; j<n; j++){
                        int aux =r.nextInt(3*n);//el rango de los numeros aleatorios
                        A[i][j]= (int)aux;      //es entre 0 y 3*n
                    }
                }
            for(int i =0; i<n;i++)
                b[i]=r.nextInt();
        }else{
                for(int i =0; i<n; i++){
                    for(int j=0; j<n; j++){
                        System.out.print("A["+(i+1)+"]["+ (j+1) +"]= ");
                        A[i][j]= Leer.datoDouble();
                        System.out.println();
                    }
                       
                }
                System.out.println("Ingrese la Matriz b (matriz solucion)");
                for(int i =0;i<n;i++){
                    System.out.print("b["+(i+1)+"]");
                    b[i]= Leer.datoDouble();
                    System.out.println();
                }
         }
    }

    /********************************
 *
 *Metodo aplicar Gauss , nos permite utlizar y ver como funciona el algoritmo de Gauss
 *a traves de un Pivote, la forma de funcionar de este algoritmo es:
 *- Encontrar el mayor de cada columna,y lo vamos almacenando en la diagonal
 *- Hacemos cero los coeficientes bajo la diagonal
 *- solucionamos el sistema para encontrar las soluciones
 *- Imprimimos las matrices
 */
    public static void Gauss(double[][] matrizA,double[] matrizB,int N_Incognitas){

	int N_Fila;//variable que nos permite almacenar la fila de coeficiente mayoe
	for (int i=0; i<N_Incognitas; i++){//manajos el numero de columna
		/*buscamos el numero de fila del pivote
	     *para pooder hacer el cambio de fila si es necesario*/
  	      N_Fila=maximo(matrizA,i,N_Incognitas);
  	    /*hacemos los cambios de fila tanto para la matrizA como para la B*/
		  matrizA = CambiarFilaMatrizA(matrizA,i,N_Fila,N_Incognitas);		
		  matrizB = CambiarFilaMatrizB(matrizB,i,N_Fila);		
		/*encontramos las soluciones del sistema*/		
		ResolucionSistema(matrizA,matrizB,i,N_Incognitas);//cada vez que encontramos el pivote
	}
	
    //Obtener los resultados de las variables
    X = MatrizSolucion (matrizA,matrizB,N_Incognitas,X);
    
    //imprimimos las matrices
	
	  System.out.println("La MATRIZ A ES");//imprimimos la matriz A
	  for(int i=0;i< n;i++){ //vamos manejando las filas
    	for(int j=0;j<n;j++){//vamos manejando las columnas
                //redondeamos los resultados a 3 decimales despues de la coma
    		A[i][j] = (double)Math.round(A[i][j]*1000) /1000;
                
                System.out.print(" "+A[i][j]+"");//imprimimos la matriz
    	}
    	System.out.println(" | ");
    }
	System.out.println("");
	
        
        System.out.println("LA MATRIZ B ES");//imprimimos la matriz B
    	for(int j=0;j<n;j++){//recorremos las filas
            //redondeamos los resultados a 3 decimales despues de la coma
    		b[j] = (double)Math.round(b[j]*1000) /1000;
    		System.out.println(" b"+(j+1)+" = "+b[j]+"");//imprimimos la matriz
    	}
    	System.out.println("");//imprimimos un salto de linea
        
        System.out.println("LA SOLUCION ES");//imprimimos la matriz X
    	for(int j=0;j< n;j++){//recorremos las filas
                //redondeamos los resultados a 3 decimales despues de la coma
                X[j] = (double)Math.round(X[j]*1000) /1000;
    		//imprimimos la solucion con su subindice
    		System.out.println(" X"+(j+1)+" = "+X[j]);
        }
    
    
}

static int maximo(double[][] matrizA,int i, int N_Incognitas)
{
	/* Asignamos un numero peque�o al pivote para realizar la 
    comparacion y busqueda del pivote mayor. */
	double Pivote = 10E-115;
    int N_Fila=0;//inicializamos el numero de fila con cero.
    
	for(int j=0; j< N_Incognitas; j++){// recorremos las filas para la columna i
		
		//comparamos el valor del numero en la posicion i, j de la matrizA
		//si es mayor que el pivote, en valor absoluto, entonces el pivote 
		//pasa a ser ese valor.
		if(Math.abs(matrizA[j][i])>Pivote){
		  Pivote = Math.abs(matrizA[i][j]);
		  N_Fila= j;//guardamos la fila en la que se encuentra el pivote
		}
	}
	return N_Fila;// retornamos el numero de la fila
}    
    
    /**
 *La funcion CambiarFilaMatrizA
 *
 *Nos permite cambiar las filas en el caso de que se encuentre el pivote
 *en una fila "mas abajo" de acuerdo al orden de las incognitas y asi
 *se simplifican las cosas, nos permite encontrar soluciones de una forma mas 
 *eficientes
 *
 *Los par�metros de entrada son 
 *
 *matrizA que es la matriz A,i el numero de la columna,N_Fila el numero de la fila
 *y N_Incognitas el numero de incognitas
 *
 *- Y los datos de salida, es la matriz A modificada.
 */

static double[][] CambiarFilaMatrizA(double[][] matrizA,int i,int N_Fila,int N_Incognitas)		
{
	double auxA=0;
	for(int k=0;k<N_Incognitas;k++){
	   auxA = matrizA[N_Fila][k];
	   matrizA[N_Fila][k] = matrizA[i][k];
	   matrizA[i][k] = auxA;
	}
	return matrizA;
}

/**
 *La funcion CambiarFilaMatrizB
 *Al haber modificado la matriz A, debemos modificar la matrizB
 *- Y los datos de salida, es la matriz A modificada.
 */

static double[] CambiarFilaMatrizB(double[] matrizB,int i,int N_Fila)		
{
	double auxB=0;
	auxB = matrizB[N_Fila];
	matrizB[N_Fila] = matrizB[i];
	matrizB[i] = auxB;
	
	return matrizB;
}
    
   static void ResolucionSistema(double[][] matrizA,double[] matrizB,int i,int N_Incognitas)
{
    double mul;//variable utilizada como multiplicador
  // aplicamos gauss por pivote
	for (int r = i+1;r<N_Incognitas;r++){
		mul = matrizA[r][i]/matrizA[i][i];
		//restamos la fila pivote por la otra fila en la matrizA
		for(int c=0;c<N_Incognitas;c++){
			matrizA[r][c] = matrizA[r][c] - (mul *matrizA[i][c]); 
		} 
		    //restamos la fila pivote por la otra fila en la matrizB
		    matrizB[r] = matrizB[r] - (mul*matrizB[i]); 	
		}
}
    
   static double[] MatrizSolucion (double[][] matrizA,double[] matrizB,int N_Incognita, double[] matrizX)
{
  //obtenemos la ultima solucion para luego ir "subiendo" y asi encontrar las demas
  matrizX[N_Incognita-1] = matrizB[N_Incognita-1]/matrizA[N_Incognita-1][N_Incognita-1];
  //para encontrar las demas soluciones, hacemos un ciclo......
  //vamos encontrando las soluciones de atras hacia adelante....
  for(int i=N_Incognita-2;i>=0;i--){
  	    //vamos asignanado la solucion, a traves de la resta de la matrizb que 
  	    //contiene los resultados de las ecuaciones, y se resta todo lo que se encuentra
  	    //sumando o restando al lado de la incognita , ejemplo
  	    //2X1+3X2+5X3 = 8 --> la matrizB tendra al 8, entonces seria lo siguiente
  	    //matrizX = (8 - (5X3+3X2))/2... esto es lo que se hace en la linea siguiente.
    	matrizX[i] = (matrizB[i]-Suma(matrizA,matrizX,i,N_Incognita))/matrizA[i][i];
  }
  return matrizX;//devolvemos la matriz solucion
   
   }
   static double Suma(double[][] matrizA, double[] matrizX,int i , int n)
{
	double t = 0.0;// inicializamos la variable t
	for(int j=i+1;j<n;j++){
		t = t + matrizA[i][j]*matrizX[j];
	}
	
	return t;//t es la suma final
}

   
   
   
   }
   
    
    
    
    
